package com.login.io;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.login.LoginAlphaAdapter.LoginElement;
import com.login.encryption.SecurityUtils;

import android.app.Activity;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

/**
 * Main File IO class handles the master.bin and data.bin file/io
 * @author Sean McGrail
 *
 */
public class FileIO {

	private static final String MASTER_KEY_KEY = "9999"; //hide this somehow
	private static final String MASTER_KEY = "master.bin";
	
	public static final String FILE_NAME = "data.bin";
	public static final String FILE_NAME_SD = Environment.getExternalStorageDirectory() + "/" + "data.xml";
	
	public static String loadMasterKey(Activity activity) throws Exception
	{
		FileInputStream fin = activity.openFileInput(MASTER_KEY);
		
		byte[] data = new byte[fin.available()];	//read size	
		fin.read(data);
		fin.close();
		
		return new String(SecurityUtils.decryptAES(MASTER_KEY_KEY, data), "UTF8");
		
	}
	
	public static void saveMasterKey(Activity activity, String key) throws IOException, Exception
	{
		FileOutputStream fout = activity.openFileOutput(MASTER_KEY, Context.MODE_PRIVATE);
		byte[] edata = SecurityUtils.encryptAES(MASTER_KEY_KEY, key);
		//fout.write(edata.length);
		fout.write(edata);
		fout.flush();
		fout.close();
		
	}
	
	
	public static String loadData(Activity activity, String fileName, String masterKey) throws Exception
	{
		FileInputStream fin = activity.openFileInput(fileName);
		
		byte[] data=null;
//		int size = fin.read();
//		Log.v("DEBUG", "Read: " + String.valueOf(size) + " available: " + fin.available());
		data = new byte[fin.available()];
		fin.read(data);
		fin.close();
		
		return new String(SecurityUtils.decryptAES(masterKey, data));
	}
	
	public static void saveData(Activity activity, String fileName, String data, String masterKey, boolean saveExternal) throws Exception
	{
		FileOutputStream fout=null;
		byte[] edata;
		
		if(saveExternal)
		{
			fout = new FileOutputStream(new File(fileName));
			edata= data.getBytes("UTF8");
		}
		else{
			fout = activity.openFileOutput(fileName, Context.MODE_PRIVATE);
			edata = SecurityUtils.encryptAES(masterKey, data);
		}
		
//		Log.v("DEBUG", "Write: " +String.valueOf(edata.length));
//		fout.write(edata.length); //write size first
		fout.write(edata);
		fout.flush();
		fout.close();
	}
	
	public static void saveDataUnencrypted(Activity activity, String fileName, String data, boolean saveExternal) throws Exception
	{
		FileOutputStream fout=null;
		
		if(saveExternal)
		{
			fout = new FileOutputStream(new File(fileName));
		}
		else
			fout = activity.openFileOutput(fileName, Context.MODE_PRIVATE);
	
		fout.write(data.getBytes());
		fout.flush();
		fout.close();
	}
	
	
	public static class XMLFileFormat
	{
		private static final String DATA = "data";
		private static final String ALPHA = "Alpha";
		private static final String LETTER = "lt";
		private static final String LOGIN = "Login";
		private static final String NAME = "name";
		private static final String KEY = "key";
		private static final String VALUE = "value";
		
		
		public static String dataToXML(HashMap<String, Vector<LoginElement>> values)
		{
			Document doc = getXML(values);
			if(doc == null){return null;}
			
			StringWriter sw = new StringWriter();
			
	        // Use a Transformer for output
	        TransformerFactory transformerFactory = TransformerFactory.newInstance();
	        Transformer transformer;
	        try {
	            transformer = transformerFactory.newTransformer();
	        }
	        catch (TransformerConfigurationException e) {
	        	System.out.println(e);
				return null;
	        }
	        DOMSource source = new DOMSource(doc);
	        StreamResult result = new StreamResult(sw);
	        // transform source into result will do save
	        try {
	            transformer.transform(source, result);
	        }
	        catch (TransformerException e) {
	        	System.out.println(e);
				return null;
	       }
	        
	        return sw.toString().trim();
		}
		
		
		
		private static Document getXML(HashMap<String, Vector<LoginElement>> values)
		{
			Document doc= null;
			   DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			   DocumentBuilder parser;
			try {
				parser = factory.newDocumentBuilder();
				doc = parser.newDocument();
			} catch (ParserConfigurationException e) {
				System.out.println(e);
				return null;
			}
			
			Node root = doc.appendChild(doc.createElement(DATA));
			
			//add all the accounts
			for(String a: values.keySet())
			{
				Node accountNode = root.appendChild(doc.createElement(ALPHA));
				((Element)accountNode).setAttribute(LETTER, a); //write name
				
				for(LoginElement e: values.get(a))
				{
					Node eNode = accountNode.appendChild(doc.createElement(LOGIN));
					
					((Element)eNode).setAttribute(NAME, e.name);
					((Element)eNode).setAttribute(KEY, e.key);
					((Element)eNode).setAttribute(VALUE, e.value);
				}
				
			}
			
			return doc;
		}
		
		
		
		public static HashMap<String, Vector<LoginElement>> xmlToData(String string) 
		throws ParserConfigurationException, SAXException, IOException
		{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder parser;
			parser = factory.newDocumentBuilder();
			return parse(parser.parse(new ByteArrayInputStream(string.getBytes())));
		}
		
		
	private static HashMap<String, Vector<LoginElement>> parse(Document doc)
	{
		HashMap<String, Vector<LoginElement>> data = new HashMap<String, Vector<LoginElement>>();
		Vector<LoginElement> currentSet=null;
		String currentLetter = null;
		
		NodeList nl = doc.getDocumentElement().getChildNodes();
		for(int i=0 ; i < nl.getLength(); i++)
		{
			if(nl.item(i).getNodeType() == Node.ELEMENT_NODE)
			{
				if(nl.item(i).getNodeName().equals(ALPHA))
				{
					Node n = nl.item(i).getAttributes().getNamedItem(LETTER);
					
					if(n != null){

					currentLetter = n.getNodeValue();
					currentSet = new Vector<LoginElement>();
					
					NodeList exps = nl.item(i).getChildNodes();
					for(int j=0; j < exps.getLength(); j++)
					{
						if(exps.item(j).getNodeType() == Node.ELEMENT_NODE)
						{
						  
						  if(exps.item(j).getNodeName().equals(LOGIN))
						  {
							Node name  = exps.item(j).getAttributes().getNamedItem(NAME);
							Node key  = exps.item(j).getAttributes().getNamedItem(KEY);
							Node value  = exps.item(j).getAttributes().getNamedItem(VALUE);
							
							
							if(name != null && key != null && value != null)
							{
								LoginElement e = new LoginElement(name.getNodeValue(), key.getNodeValue(), value.getNodeValue());
								currentSet.add(e);
							}
						  }
						}
					}
					if(currentSet != null && currentLetter !=null)
					{
						Collections.sort(currentSet);
						data.put(currentLetter, currentSet);
					}
					}
				}
			}
		}
		
		return data;
	}
	
	}
	
	
}
